/*
 * To change this template, choose Tools | Templates
 * and open the template in the editor.
 */
package fileidentifier.javaclassutils;

import java.io.DataInputStream;
import java.io.File;
import java.io.FileInputStream;
import java.io.FileNotFoundException;
import java.io.IOException;
import java.lang.reflect.Field;
import java.lang.reflect.Method;
import java.util.Arrays;
import java.util.HashMap;
import java.util.Map;

/**
 *
 * @author ApT
 */
public class JavaClassUtils {

    public static final Map<String, String> JAVA_VERSION_TEMPLATE = new HashMap<String, String>();
    public static final String JAVA_FILE_TEMPLATE = "CAFEBABE";

    static {
        JAVA_VERSION_TEMPLATE.put("33", "J2SE 7");
        JAVA_VERSION_TEMPLATE.put("32", "J2SE 6.0");
        JAVA_VERSION_TEMPLATE.put("31", "J2SE 5.0");
        JAVA_VERSION_TEMPLATE.put("30", "JDK 1.4");
        JAVA_VERSION_TEMPLATE.put("2F", "JDK 1.3");
        JAVA_VERSION_TEMPLATE.put("2E", "JDK 1.2");
        JAVA_VERSION_TEMPLATE.put("2D", "JDK 1.1");
    }
    private File file;
    private byte[] fileArray;
    private DataInputStream dis;
    private ModuleLoader loader;
    private Class loadedClass;

    public JavaClassUtils(String fileName) throws FileNotFoundException, IOException {
        file = new File(fileName);
        fileArray = new byte[(int) file.length()];
        dis = new DataInputStream(new FileInputStream(file));
        dis.readFully(fileArray);
        dis.close();
        if (checkFormat()) {
            try {
                loader = new ModuleLoader(file.getParent(), ClassLoader.getSystemClassLoader());
                loadedClass = loader.findClass(fileArray);
            } catch (Exception ex) {
                System.out.println(ex.toString());
                loader = null;
                loadedClass = null;
            }
        }
    }

    public boolean checkFormat() {
        String template = formatByte(Arrays.copyOfRange(fileArray, 0, 4));
        if (template.equalsIgnoreCase(JAVA_FILE_TEMPLATE)) {
            return true;
        }
        return false;
    }

    public String getJavaVersion() {
        String template = formatByte(fileArray[7]);
        String result = JAVA_VERSION_TEMPLATE.get(template);
        return result;
    }

    public String getIdentifier() {
        return formatByte(Arrays.copyOfRange(fileArray, 0, 4));
    }

    public String getClassName() {
        if (loadedClass != null) {
            return loadedClass.getCanonicalName();
        }
        return null;
    }

    public String getAllMethods() {
        String result = "";
        int i = 1;
        for (Method method : loadedClass.getMethods()) {
            result += "\t" + i + ". " + method + ";\n";
            i++;
        }
        return result;
    }

    public String getAllFields() {
        String result = "";
        int i = 1;
        for (Field field : loadedClass.getFields()) {
            result += "\t" + i + ". " + field + ";\n";
            i++;
        }
        return result;
    }

    private String formatByte(byte b) {
        return String.format("%02X ", b).trim();
    }

    public String compileInfo() {
        if (checkFormat()) {
            return "CLASS file information:\n\tIdentifier:\t" + getIdentifier() + "\n\tClass name:\t"
                    + getClassName() + "\n\tJava version:\t" + getJavaVersion() + "\n\tMethods:\n"
                    + getAllMethods() + "\tFields:\n" + getAllFields();
        } else {
            return "This is not .CLASS file";
        }
    }

    private String formatByte(byte[] b) {
        String resultString = "";
        for (int i = 0; i < b.length; i++) {
            resultString += String.format("%02X ", b[i]);
        }
        resultString = resultString.replaceAll(" ", "");
        return resultString;
    }
}
